#include "confirmdialog.h"
#include "gui.h"

#include <QApplication>
#include <QDebug>
#include <QDir>
#include <QEventLoop>
#include <QFile>
#include <QFileInfo>
#include <QList>
#include <QProcess>
#include <QRegExp>
#include <QThread>
#include <QTimer>
#include <QTranslator>

struct DesktopFile
{
  QString filename;
  QString name;
  QString command;
  QString info;

  bool parse( const QString& filename );
};

bool DesktopFile::parse( const QString& filename  )
{
  this->filename = filename;
  this->name = QFileInfo(filename).fileName();
  this->command = QString();
  this->info = QString();

  QFile file(filename);
  if( !file.open(QIODevice::ReadOnly) ) {

    return false;
  }

  QRegExp reLine("^([^\\s#=]+)\\s*=\\s*([^#]+)");
  QRegExp reCmd("([^\\s\"']+|\"([^\"]+)\"|'([^']+)')");

  for( ;; ) {

    QString line = QString::fromUtf8( file.readLine() );
    if( line.isEmpty() )
      break;

    if( reLine.indexIn( line.trimmed() ) < 0 ) 
      continue;

    QString key = reLine.cap(1);
    QString value = reLine.cap(2).trimmed();
    
    if( key == "Name" ) {

      this->name = value;
    }
    else if( key == "Exec" ) {

      if( reCmd.indexIn( value ) == 0 )
	this->command = reCmd.cap(3).isEmpty() ? (reCmd.cap(2).isEmpty() ? reCmd.cap(1) : reCmd.cap(2)) : reCmd.cap(3);
    }
  }

  return !command.isEmpty();
}

static bool checkCommand( const QString& command, QString* hint )
{
  if( command.startsWith( "/" ) ) {

    if( !QFile::exists( command ) ) {

      *hint = qApp->translate("Gui","Executable not found: \"%1\"").arg(command);
      return false;
    }

    return true;
  }
  else {

    QStringList pathParts = QString(getenv("PATH")).split(":");
    for( QStringList::const_iterator it = pathParts.begin();
	 it != pathParts.end(); ++it ) {

      QString path = (*it).trimmed();
      if( path.isEmpty() )
	continue;

      if( QFile::exists( path + "/" + command ) )
	return true;
    }

    *hint = qApp->translate("Gui","Command not on $PATH: \"%1\"").arg(command);
    return false;
  }
}
 
static void findDesktopFiles( const QString& path, QList<DesktopFile>* list )
{
  QDir dir(path);
  QFileInfoList infos( dir.entryInfoList( QStringList() << "*.desktop", 
					  QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot ) );
  for( QFileInfoList::const_iterator it = infos.begin();
       it != infos.end(); ++it ) {
    
    if( (*it).isDir() ) {

      findDesktopFiles( (*it).absoluteFilePath(), list );
    }
    else {

      DesktopFile file;
      if( file.parse( (*it).absoluteFilePath() ) && !checkCommand( file.command, &file.info ) ) {
	
	list->append( file );
      }
    }
  }
}

class FinderThread : public QThread
{
public:
  void run();

  QList< DesktopFile > files;
};

void FinderThread::run()
{
  findDesktopFiles( QDir::homePath() + "/.local/share/applications", &files );
}

class UninstallerThread : public QThread
{
public:
  UninstallerThread( const QStringList& filenames );
  void run();

  QStringList filenames;
};

UninstallerThread::UninstallerThread( const QStringList& filenames )
  : filenames(filenames)
{
}

void UninstallerThread::run()
{
  for( QStringList::const_iterator it = filenames.begin();
       it != filenames.end(); ++it ) {

    QString basename = QFileInfo(*it).fileName();
    
    QProcess::execute( QString("xdg-desktop-menu uninstall --noupdate \"%1\"").arg(basename) );
    if( QFile::exists( *it ) ) {

      QProcess::execute( QString("rm \"%1\"").arg(*it) );
    }
  }

  QProcess::execute( QString("xdg-desktop-menu forceupdate") );
}

struct Item : public QTreeWidgetItem
{
  Item( QTreeWidget* parent )
    : QTreeWidgetItem(parent)
  {
  }

  bool operator<( const QTreeWidgetItem& that ) const
  {
    return text( treeWidget()->sortColumn() ).toLower()
      .localeAwareCompare( that.text( treeWidget()->sortColumn() ).toLower() ) < 0;
  }
};

Gui::Gui()
{
  _ui.setupUi(this);

  _ui.treeWidget->sortItems(0, Qt::AscendingOrder);

  connect( _ui.treeWidget, SIGNAL(itemSelectionChanged()), SLOT(onSelectionChanged()) );
  connect( _ui.pushButton, SIGNAL(clicked()), SLOT(onDeleteClicked()) );

  refreshList();
}

void Gui::onSelectionChanged()
{
  _ui.pushButton->setEnabled( !_ui.treeWidget->selectedItems().isEmpty() );
}

void Gui::onDeleteClicked()
{
  QStringList filenames;
  QList< QTreeWidgetItem* > selected( _ui.treeWidget->selectedItems() );
  for( QList< QTreeWidgetItem* >::const_iterator it = selected.begin();
       it != selected.end(); ++it ) {
    
    filenames.append( (*it)->data(0, Qt::UserRole).toString() );
  }

  if( filenames.isEmpty() )
    return;

  if( ConfirmDialog(this).exec() != QDialog::Accepted )
    return;

  _ui.label->setText("Deleting files...");

  setEnabled( false );
  qApp->setOverrideCursor(Qt::BusyCursor);

  QEventLoop loop;
  {
    UninstallerThread thread(filenames);
    connect( &thread, SIGNAL(finished()), &loop, SLOT(quit()) );

    QTimer::singleShot( 0, &thread, SLOT(start()) );
    loop.exec();
  }    

  qApp->restoreOverrideCursor();
  setEnabled(true);

  refreshList();
}

void Gui::refreshList()
{
  _ui.label->setText("Refreshing file list...");

  setEnabled( false );
  qApp->setOverrideCursor(Qt::BusyCursor);

  QEventLoop loop;
  {
    FinderThread thread;
    connect( &thread, SIGNAL( finished() ), &loop, SLOT(quit()) );
    
    QTimer::singleShot( 0, &thread, SLOT(start()) );
    loop.exec();
    
    _ui.treeWidget->clear();
    
    for( QList<DesktopFile>::const_iterator it = thread.files.begin();
	 it != thread.files.end(); ++it ) {
      
      Item* item = new Item( _ui.treeWidget );
      item->setData(0, Qt::UserRole, (*it).filename);
      item->setText(0, (*it).name );
      item->setText(1, QFileInfo((*it).filename).fileName());
      item->setText(2, (*it).info );
    }
  }
  
  qApp->restoreOverrideCursor();
  setEnabled(true);

  int n = _ui.treeWidget->topLevelItemCount();
  _ui.label->setText( n ? tr("%n entries found.", "", n) : tr("No stale entries found.") );
}

int main( int argc, char** argv )
{
  QApplication app(argc, argv);

  qDebug() << "Version:" << VERSION;

  {
    QTranslator* translator = new QTranslator;
    QString suffix = QLocale::system().name();
    int i = suffix.indexOf("_");
    if( i ) 
      suffix = suffix.left(i);
    
    if( !translator->load( QString(":/menu-cleaner_%1").arg( suffix ) ) &&
	!translator->load( QString(":/menu-cleaner_en") ) ) {
      
      delete translator;
    }
    else {
      
      app.installTranslator( translator );
    }
  }

  Gui* gui = new Gui();
  gui->show();
  app.exec();
  return 0;
}
